package engine;

/*
 * Schedule 1 Add/Addi/nand/beq/call/ret/beq/jmp
 * Schedule 2 Mul/Div
 * Schedule 3 Load
 * Schedule 4 store
 */




import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;

import engine.Cache.CacheOutputPack;


public class Main {
	//latencies
	public static  int ADDTIME = 2;
	public static  int MULTIME = 2;
	
	//no of reservation stations for each unit
	
	public static  int ADDRS = 0;
	public static  int MULRS = 0;
	public static  int STRS = 0;
	public static  int LDRS = 0;
	
	public InstructionBuffer instBuffer;
	public static int InstructionBufferSize;
	
	public int counter;
	public int beginfetchIndex;
	//String tempWord;
	boolean CDBAvailable=true;
	
	public static  int memorySize;
	
	public static int n;
	
	public static int ROBEntries;
	public static String before="";
	
	//public static Memory mem;
	
	public static int clock;
	public static int pc;
	public static int basePC;
	
	//ArrayList<Instruction> instList;
	public static RegisterSet register;
	public static RS reservationStations;
	public static LoadProgram p;
	public static ArrayList<Instruction> allinstructions;
	//public static ArrayList<Instruction> instructions;
	public static ArrayList<Instruction> execList;
	public static ArrayList<Instruction> nextWbList;
	public static ArrayList<Instruction> wbList;
	public static ArrayList<Instruction> nextCommitList;
	public static ArrayList<Instruction> commitList;
	//public static ArrayList<Instruction> nextCommit;
	public static String file;
	public static ROB rob;
	
	Instruction nextToExec = null;
	InstructionSet instructionset;
	
	public static Cache Datac;
	public static Memory Datam;
	public static Cache Data2c;
	public static Cache Data3c;
	
	public static int noDataCaches;
	
	// Data Memory dimensions.
	public static int dmS;
	public static int dmLatency;
	
	//Data cache1 dimensions
	public static int dc1S;
	public static int dc1L;
	public static int dc1M;
	public static int dc1HWPolicy;
	public static int dc1MWPolicy;
	public static int dc1Latency;
	
	//Data cache2 dimensions
	public static int dc2S;
	public static int dc2L;
	public static int dc2M;
	public static int dc2HWPolicy;
	public static int dc2MWPolicy;
	public static int dc2Latency;
	
	//Data cache3 dimensions
	public static int dc3S;
	public static int dc3L;
	public static int dc3M;
	public static int dc3HWPolicy;
	public static int dc3MWPolicy;
	public static int dc3Latency;
	
	
	public static Cache InstCache;
	public static  Memory InstMem;
	public static Cache Inst2cache;
	public static Cache Inst3cache;
	public static int noInstCache;
	
	
	// Instruction Memory dimensions.
		public static int imS;
		public static int imLatency;
		
		//Instruction cache1 dimensions
		public static int ic1S;
		public static int ic1L;
		public static int ic1M;
		public static int ic1HWPolicy;
		public static int ic1MWPolicy;
		public static int ic1Latency;
		
		//Instruction cache2 dimensions
		public static int ic2S;
		public static int ic2L;
		public static int ic2M;
		public static int ic2HWPolicy;
		public static int ic2MWPolicy;
		public static int ic2Latency;
		
		//Instruction cache3 dimensions
		public static int ic3S;
		public static int ic3L;
		public static int ic3M;
		public static int ic3HWPolicy;
		public static int ic3MWPolicy;
		public static int ic3Latency;

	
	public Instruction beingFetched;
	

	public ArrayList<String> instructionsString;
	public boolean fetch;
	public int endPc;
	public static ArrayList<Done> doneInstructions;
	//public Queue<LoadStoreObject> loadStoreQueue;
	public boolean beginfetch;
	
	public Main() {
		clock = 0;
		pc = 0;
		//mem=new Memory(memorySize);
		rob=new ROB(ROBEntries);
		
		switch(noDataCaches) {
		case 1:
			Datac = new Cache(dc1S, dc1L, dc1M, dc1HWPolicy, dc1MWPolicy, dc1Latency);
			Datam = new Memory(dmS,dc1L,dmLatency);
			Datac.setNextLevel(Datam);
		break;
		case 2:
			Datac = new Cache(dc1S, dc1L, dc1M, dc1HWPolicy, dc1MWPolicy, dc1Latency);
			Data2c = new Cache(dc2S, dc2L, dc2M, dc2HWPolicy, dc2MWPolicy, dc2Latency);
			Datam = new Memory(dmS,dc1L,dmLatency);
			Datac.setNextLevel(Data2c);
			Data2c.setNextLevel(Datam);
		break;
		case 3:
			Datac = new Cache(dc1S, dc1L, dc1M, dc1HWPolicy, dc1MWPolicy, dc1Latency);
			Data2c = new Cache(dc2S, dc2L, dc2M, dc2HWPolicy, dc2MWPolicy, dc2Latency);
			Data3c = new Cache(dc3S, dc3L, dc3M, dc3HWPolicy, dc3MWPolicy, dc3Latency);
			Datam = new Memory(dmS,dc1L,dmLatency);
			Datac.setNextLevel(Data2c);
			Data2c.setNextLevel(Data3c);
			Data3c.setNextLevel(Datam);
		break;
		default:
			Datac = new Cache(4, 2, 2, 0, 1,1);
			Datam = new Memory(32,2,1);
			Datac.setNextLevel(Datam);
		break;
		}
		
		switch(noInstCache) {
		case 1:
			InstCache = new Cache(ic1S, ic1L, ic1M, ic1HWPolicy, ic1MWPolicy, ic1Latency);
			InstMem = new Memory(imS,ic1L,imLatency);
			InstCache.setNextLevel(InstMem);
		break;
		case 2:
			InstCache = new Cache(ic1S, ic1L, ic1M, ic1HWPolicy, ic1MWPolicy, ic1Latency);
			Inst2cache = new Cache(ic2S, ic2L, ic2M, ic2HWPolicy, ic2MWPolicy, ic2Latency);
			InstMem = new Memory(imS,ic1L,imLatency);
			InstCache.setNextLevel(Inst2cache);
			Inst2cache.setNextLevel(InstMem);
		break;
		case 3:
			InstCache = new Cache(ic1S, ic1L, ic1M, ic1HWPolicy, ic1MWPolicy, ic1Latency);
			Inst2cache = new Cache(ic2S, ic2L, ic2M, ic2HWPolicy, ic2MWPolicy, ic2Latency);
			Inst3cache = new Cache(ic3S, ic3L, ic3M, ic3HWPolicy, ic3MWPolicy, ic3Latency);
			InstMem = new Memory(imS,ic1L,imLatency);
			InstCache.setNextLevel(Inst2cache);
			Inst2cache.setNextLevel(Inst3cache);
			Inst3cache.setNextLevel(InstMem);
		break;
		default:
			InstCache = new Cache(4, 2, 2, 0, 1,1);
			InstMem = new Memory(32,2,1);
			InstCache.setNextLevel(InstMem);
		break;
		}
		

		instBuffer=new InstructionBuffer(InstructionBufferSize, basePC);
		
		
		execList=new ArrayList<Instruction>();
		nextWbList=new ArrayList<Instruction>();
		wbList=new ArrayList<Instruction>();
		nextCommitList=new ArrayList<Instruction>();
		commitList=new ArrayList<Instruction>();
		register=new RegisterSet();
		beingFetched=null;
		//next
		try {
			reservationStations=new RS(ADDRS, MULRS, LDRS, STRS, n);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//fetch=true;
		
	}
	public void loadCode() throws IOException {
		p=new LoadProgram(basePC);
		p.loadContents(file);
		doneInstructions=new ArrayList<Done>();
		instructionsString=p.list;
		instructionset=p.instructionset;
		//allinstructions=instructionset.instructions;
		pc=instructionset.basePC;
		
		allinstructions = instructionset.instructions;
		for(int i=0;i<instructionsString.size();i++) {
			InstMem.StoreWord(basePC+i, instructionsString.get(i) );
		}
		endPc=basePC+allinstructions.size();
		beginfetch=true;
	}
	public void runAll(){
	
		boolean end = false;
		while (!end){
			step();
			if(doneInstructions.size()>0) {
				if(doneInstructions.get(doneInstructions.size()-1).opcode.equals("END")) {
					end=true;
				}
			}
		/*	if(instructions.get(instructions.size()-1).opcode.equals("END") && instructions.get(instructions.size()-1).commit!=0
					&&instructions.get(instructions.size()-1).commit<=clock) {
				end=true;
				
			}
			*/
		}
		
	}
	public void step() {
		clock ++;
		if(beginfetch) {
			instBuffer=new InstructionBuffer(InstructionBufferSize, pc);
			fetch=true;
			beginfetch=false;
			beginfetchIndex=pc;
			counter=0;
		}
		if(fetch) {
			fetch();
		} 
		issue();
		
		
		execute();
		write();
		commit();
		if (nextToExec != null){
			execList.add(nextToExec);
		} 
		if (nextWbList.size() != 0){
			for (Instruction inst: nextWbList){
				wbList.add(inst);
			}
			nextWbList.clear();
		}
		if (nextCommitList.size() != 0){
			for (Instruction inst: nextCommitList){
				commitList.add(inst);
			}
			nextCommitList.clear();
		} 
	}
	public void fetch() {
		if(fetch) {
			//clock--;
			//if(inst)
			//CacheOutputPack out
			if(beginfetchIndex+counter>=endPc || beginfetchIndex+counter>=instBuffer.basePc+InstructionBufferSize) {
				fetch=false;
				return;
			}
			if(beingFetched==null) {
				if(beginfetchIndex+counter<basePC+allinstructions.size()) {
					CacheOutputPack out=InstCache.getLoadLatency(beginfetchIndex+counter);
					beingFetched=p.decode(out.word);
					beingFetched.timefetch=out.latency;
					beingFetched.timefetch--;
				}
			} else {
				beingFetched.timefetch--;
			}
			if(beingFetched.timefetch==0) {
				instBuffer.addInstruction(beingFetched, beingFetched.instString,clock);
				counter++;
				beingFetched=null;
			}
		}
	}
	public void issue() {
		if(before.equals("br") || before.equals("end")) {
			return;
		}
		if(pc>=endPc) {
			nextToExec=null;
			return;
		}
		if(pc<instBuffer.basePc || pc>=instBuffer.basePc+InstructionBufferSize) {
			beginfetch = true;
			nextToExec=null;
			return;
		}
              
		if (fetch) {
			nextToExec = null;
			return;
		}
		if(rob.valid[rob.tail]) {
			nextToExec = null;
			return;
		}
		
		Instruction inst = instBuffer.getInstruction(pc);
		nextToExec = null;
		if(inst.opcode.equals("END")) {
			inst.time=1;
			int station = schedule(1);
			if(station==-1) {
				nextToExec = null;
				return;
			}
			int num=rob.addEntry("BR", "",station);
			rob.entries[num].inst=inst;
			inst.rob=num;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			//register.setStation(inst.rt, inst.station);
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			nextToExec = inst;
			
			inst.issue = clock;
			pc++;
                        before="end";
			return;
		}
		if (inst.opcode.equals("ADD") || inst.opcode.equals("NAND")) {
			inst.time=ADDTIME;
			int station = schedule(1);
			if(station==-1) {
				return;
			}
			int num=rob.addEntry("FP", inst.rd,station);
			rob.entries[num].inst=inst;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			inst.rob=num;
			
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			int src1Station = register.getStation(inst.rs);
			int src2Station = register.getStation(inst.rt);
			if (src1Station == -1) {
				register.setStation(inst.rs, -1);
				reservationStations.entries[station].vj=register.read(inst.rs);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src1Station == num) {
				reservationStations.entries[station].vj=register.read(inst.rs);
			}
			else if(rob.entries[src1Station].ready) {
				reservationStations.entries[station].vj=rob.entries[src1Station].value;
			}
			else {
				reservationStations.entries[station].qj=src1Station+"";
			}
			if (src2Station == -1) {
				register.setStation(inst.rt, -1);
				reservationStations.entries[station].vk=register.read(inst.rt);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src2Station == num) {
				reservationStations.entries[station].vk=register.read(inst.rt);
			}
			else if(rob.entries[src2Station].ready) {
				reservationStations.entries[station].vk=rob.entries[src2Station].value;
			}
			else {
				reservationStations.entries[station].qk=src2Station+"";
			}
			register.setStation(inst.rd, num);
			pc ++;
		}
		if (inst.opcode.equals("ADDI")) {
			inst.time=ADDTIME;
			int station = schedule(1);
			if(station==-1) {
				return;
			}
			int num=rob.addEntry("FP", inst.rd,station);
			rob.entries[num].inst=inst;
			inst.rob=num;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			int src1Station = register.getStation(inst.rs);
			//int src2Station = register.getStation(inst.rt);
			if (src1Station == -1) {
				register.setStation(inst.rs, -1);
				reservationStations.entries[station].vj=register.read(inst.rs);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src1Station==num) {
				reservationStations.entries[station].vj=register.read(inst.rs);
			}
			else if(rob.entries[src1Station].ready) {
				reservationStations.entries[station].vj=rob.entries[src1Station].value;
			}
			else {
				reservationStations.entries[station].qj=src1Station+"";
			}
			reservationStations.entries[station].A=Double.parseDouble(inst.imm);
			register.setStation(inst.rd, num);
			pc ++;
		}
		if (inst.opcode.equals("MUL")||inst.opcode.equals("DIV")) {
			inst.time=MULTIME;
			int station = schedule(2);
			if(station==-1) {
				return;
			}
			int num=rob.addEntry("FP", inst.rd,station);
			inst.rob=num;
			rob.entries[num].inst=inst;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			int src1Station = register.getStation(inst.rs);
			int src2Station = register.getStation(inst.rt);
			if (src1Station == -1) {
				register.setStation(inst.rs, -1);
				reservationStations.entries[station].vj=register.read(inst.rs);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src1Station == num) {
				reservationStations.entries[station].vj=register.read(inst.rs);

			}
			else if(rob.entries[src1Station].ready) {
				reservationStations.entries[station].vj=rob.entries[src1Station].value;
			}
			else {
				reservationStations.entries[station].qj=src1Station+"";
			}
			if (src2Station == -1) {
				register.setStation(inst.rt, -1);
				reservationStations.entries[station].vk=register.read(inst.rt);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src2Station == num) {
				reservationStations.entries[station].vk=register.read(inst.rt);

			}
			else if(rob.entries[src2Station].ready) {
				reservationStations.entries[station].vk=rob.entries[src2Station].value;
			}
			else {
				reservationStations.entries[station].qk=src2Station+"";
			}
			register.setStation(inst.rd, num);
			pc ++;
		}
		if(inst.opcode.equals("LW")) {
			inst.time=Integer.MAX_VALUE;
			int station = schedule(3);
			if(station==-1) {
				nextToExec=null;
				return;
			}
			int num=rob.addEntry("LW", inst.rt,station);
			rob.entries[num].inst=inst;
			inst.rob=num;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			
			int src1Station = register.getStation(inst.rs);
			if (src1Station == -1 ) {
				register.setStation(inst.rs, -1);
				reservationStations.entries[station].vj=register.read(inst.rs);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src1Station == num) {
				reservationStations.entries[station].vj=register.read(inst.rs);

			}
			else if(rob.entries[src1Station].ready) {
				reservationStations.entries[station].vj=rob.entries[src1Station].value;
			}
			else {
				reservationStations.entries[station].qj=src1Station+"";
			}
			reservationStations.entries[station].A=Double.parseDouble(inst.imm);
			register.setStation(inst.rt, num);
			pc ++;
			
		}
		if(inst.opcode.equals("SW")) {
			inst.time=1;
			int station = schedule(4);
			if(station==-1) {
				return;
			}
			int num=rob.addEntry("SW", "",station);
			rob.entries[num].inst=inst;
			inst.rob=num;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			//register.setStation(inst.rt, inst.station);
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			
		
			
			int src1Station = register.getStation(inst.rs);
			int src2Station = register.getStation(inst.rt);
			if (src1Station == -1) {
				register.setStation(inst.rs, -1);
				reservationStations.entries[station].vj=register.read(inst.rs);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src1Station == num) {
				reservationStations.entries[station].vj=register.read(inst.rs);
			}
			else if(rob.entries[src1Station].ready) {
				reservationStations.entries[station].vj=rob.entries[src1Station].value;
			}
			else {
				reservationStations.entries[station].qj=src1Station+"";
			}
			if (src2Station == -1) {
				register.setStation(inst.rt, -1);
				reservationStations.entries[station].vk=register.read(inst.rt);
				rob.entries[num].Dest=(int)(reservationStations.entries[station].vk+Double.parseDouble(inst.imm))+"";
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if( src2Station == num) {
				reservationStations.entries[station].vk=register.read(inst.rt);
				rob.entries[num].Dest=(int)(reservationStations.entries[station].vk+Double.parseDouble(inst.imm))+"";
				
			}
			else if(rob.entries[src2Station].ready) {
				reservationStations.entries[station].vk=rob.entries[src2Station].value;
				rob.entries[num].Dest=(int)(reservationStations.entries[station].vk+Double.parseDouble(inst.imm))+"";

			}
			else {
				reservationStations.entries[station].qk=src2Station+"";
			}
			reservationStations.entries[station].A=Double.parseDouble(inst.imm);
			
			pc ++;
			
		}
		if(inst.opcode.equals("JMP")) {
			inst.time=1;
			int station = schedule(1);
			if(station==-1) {
				return;
			}
			int num=rob.addEntry("BR", "",station);
			rob.entries[num].inst=inst;
			inst.rob=num;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			//register.setStation(inst.rt, inst.station);
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			int src1Station = register.getStation(inst.rs);
			if (src1Station == -1) {
				register.setStation(inst.rs, -1);
				reservationStations.entries[station].vj=register.read(inst.rs);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src1Station == num) {
				reservationStations.entries[station].vj=register.read(inst.rs);
			}
			else if(rob.entries[src1Station].ready) {
				reservationStations.entries[station].vj=rob.entries[src1Station].value;
			}
			else {
				reservationStations.entries[station].qj=src1Station+"";
			}
			reservationStations.entries[station].A=Double.parseDouble(inst.imm);
			reservationStations.entries[station].vk=pc;
			//pc ++;
			nextToExec = inst;
			
			inst.issue = clock;
			before="br";
			return;
			
		}
		if(inst.opcode.equals("RET")) {
			inst.time=1;
			int station = schedule(1);
			if(station==-1) {
				return;
			}
			int num=rob.addEntry("BR", "",station);
			rob.entries[num].inst=inst;
			inst.rob=num;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			//register.setStation(inst.rt, inst.station);
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			int src1Station = register.getStation(inst.rs);
			if (src1Station == -1) {
				register.setStation(inst.rs, -1);
				reservationStations.entries[station].vj=register.read(inst.rs);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src1Station == num) {
				reservationStations.entries[station].vj=register.read(inst.rs);
			}
			else if(rob.entries[src1Station].ready) {
				reservationStations.entries[station].vj=rob.entries[src1Station].value;
			}
			else {
				reservationStations.entries[station].qj=src1Station+"";
			}
			//reservationStations.entries[station].A=Double.parseDouble(inst.imm);
			
			//pc ++;
			nextToExec = inst;		
			inst.issue = clock;
			before="br";
			return;
			
		}
		if(inst.opcode.equals("JALR")) {
			inst.time=1;
			int station = schedule(1);
			if(station==-1) {
				return;
			}
			int num=rob.addEntry("BR", inst.rd,station);
			rob.entries[num].inst=inst;
			inst.rob=num;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			int src1Station = register.getStation(inst.rs);
			if (src1Station == -1 ) {
				register.setStation(inst.rs, -1);
				reservationStations.entries[station].vj=register.read(inst.rs);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src1Station == num) {
				reservationStations.entries[station].vj=register.read(inst.rs);

			}
			else if(rob.entries[src1Station].ready) {
				reservationStations.entries[station].vj=rob.entries[src1Station].value;
			}
			else {
				reservationStations.entries[station].qj=src1Station+"";
			}
			//reservationStations.entries[station].A=Double.parseDouble(inst.imm);
			reservationStations.entries[station].vk=(pc+1);
			register.setStation(inst.rd, num);
			//pc ++;
			nextToExec = inst;	
			//pc=
			inst.issue = clock;
			before="br";
			return;
			
		}
		if(inst.opcode.equals("BEQ")) {
			inst.time=ADDTIME;
			int station = schedule(1);
			if(station==-1) {
				return;
			}
			int num=rob.addEntry("BR", "",station);
			rob.entries[num].inst=inst;
			inst.rob=num;
			reservationStations.entries[station].dest=num;
			inst.station = station;
			reservationStations.entries[station].busy=true;
			reservationStations.entries[station].op=inst.opcode;
			int src1Station = register.getStation(inst.rs);
			int src2Station = register.getStation(inst.rt);
			if (src1Station == -1 ) {
				register.setStation(inst.rs, -1);
				reservationStations.entries[station].vj=register.read(inst.rs);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src1Station == num) {
				reservationStations.entries[station].vj=register.read(inst.rs);

			}
			else if(rob.entries[src1Station].ready) {
				reservationStations.entries[station].vj=rob.entries[src1Station].value;
			}
			else {
				reservationStations.entries[station].qj=src1Station+"";
			}
			if (src2Station == -1) {
				register.setStation(inst.rt, -1);
				reservationStations.entries[station].vk=register.read(inst.rt);
				//rs.setData1(Global.getID(station), register.read(inst.src1));
			} else if(src2Station == num) {
				reservationStations.entries[station].vk=register.read(inst.rt);

			}
			else if(rob.entries[src2Station].ready) {
				reservationStations.entries[station].vk=rob.entries[src2Station].value;
			}
			else {
				reservationStations.entries[station].qk=src2Station+"";
			}
			reservationStations.entries[station].A=Double.parseDouble(inst.imm);
			reservationStations.entries[station].pc=pc;
			inst.pc=pc;
			if(reservationStations.entries[station].A<0) {
				//pc ++;
				nextToExec = inst;		
				inst.issue = clock;
				pc=pc+1+(int)reservationStations.entries[station].A;
				inst.branchtaken=true;
                                if(before.equals("br"))
				     before="";
				return;
			} else {
				nextToExec = inst;		
				inst.issue = clock;
				pc++;
                                if(before.equals("br"))
				    before="";
				inst.branchtaken=false;
				return;
			//	pc=pc+1+(int)reservationStations.entries[station].A;
			}
		}
                if(before.equals("br"))
		   before="";
		nextToExec = inst;
		inst.issue = clock;
		return;
	}
	
	
	public void execute() {
		if (execList.size() == 0) return;
		ArrayList<Integer> delList = new ArrayList<Integer>();
		for (int i = 0; i < execList.size(); i++){
			Instruction inst = execList.get(i);
			String opcode=inst.opcode;
			RSEntry temp=reservationStations.entries[inst.station];
			if(opcode.equals("ADD")) {
				if(inst.time==ADDTIME) {
					if(temp.qj.equals("") && temp.qk.equals("")) {
						inst.time--;
						//continue;
					}
					
				} else {
					inst.time--;
				}
				if(inst.time==0) {
					float result =(float) (temp.vj + temp.vk);
					nextWbList.add(inst);						
						
					inst.result = result;
					inst.execute = clock;
					//execList.remove(inst);
					delList.add(i);
				}
			} else if(opcode.equals("END")) {
				if(inst.time==1) {
					inst.time--;
				}
				if(inst.time==0) {
					nextWbList.add(inst);
					inst.execute = clock;
					//execList.remove(inst);
					delList.add(i);
				}
			}
			else if(opcode.equals("ADDI")) {
				if(inst.time==ADDTIME) {
					if(temp.qj.equals("")) {
						inst.time--;
						//continue;
					}
					
				} else {
					inst.time--;
				}
				if(inst.time==0) {
					double result =(temp.vj + temp.A);
					nextWbList.add(inst);						
						
					inst.result = result;
					inst.execute = clock;
					//execList.remove(inst);
					delList.add(i);
				}
			}
			else if(opcode.equals("NAND")) {
				if(inst.time==ADDTIME) {
					if(temp.qj.equals("") && temp.qk.equals("")) {
						inst.time--;
						//continue;
					}
					
				} else {
					inst.time--;
				}
				if(inst.time==0) {
					double result=(~((int)temp.vj&(int)temp.vk));
					nextWbList.add(inst);						
						
					inst.result = result;
					inst.execute = clock;
					//execList.remove(inst);
					delList.add(i);
				}
			} else if(opcode.equals("MUL")) {
				if(inst.time==MULTIME) {
					if(temp.qj.equals("") && temp.qk.equals("")) {
						inst.time--;
						//continue;
					}
					
				} else {
					inst.time--;
				}
				if(inst.time==0) {
					double result =(temp.vj * temp.vk);
					nextWbList.add(inst);						
						
					inst.result = result;
					inst.execute = clock;
					//execList.remove(inst);
					delList.add(i);
				}
			} else if(opcode.equals("DIV")) {
				if(inst.time==MULTIME) {
					if(temp.qj.equals("") && temp.qk.equals("")) {
						inst.time--;
						//continue;
					}
					
				} else {
					inst.time--;
				}
				if(inst.time==0) {
					double result = (temp.vj / temp.vk);
					nextWbList.add(inst);						
						
					inst.result = result;
					inst.execute = clock;
					//execList.remove(inst);
					delList.add(i);
				}
			} else if(opcode.equals("LW")) {
				CacheOutputPack out=null;
				if(inst.time==Integer.MAX_VALUE ) {
				
					if(temp.qj.equals("")) {
						//inst.time--;
						temp.A=temp.A+temp.vj;
						temp.vj=0;
						//first=false;
						//CacheOutputPack 
						out=Datac.getLoadLatency((int)temp.A);
						inst.time=out.latency;
						inst.word=out.word;
						//inst.
						//continue;
					}
					
				} else {
					inst.time--;
				}
				if(inst.time==0) {
					//float result =(float) (temp.vj + temp.vk);
					double result=Double.parseDouble(inst.word);
					nextWbList.add(inst);						
						
					inst.result = result;
					inst.execute = clock;
					//execList.remove(inst);
					delList.add(i);
				}
			} else if(opcode.equals("SW")) {
				if(inst.time==1) {
					if(temp.qk.equals("")) {
						//inst.time--;
						temp.A=temp.A+temp.vk;
						temp.vk=0;
						rob.entries[temp.dest].Dest=temp.A+"";
						//first=false;
						//inst.time--;
						
					}
					if(temp.qj.equals("") && temp.qk.equals("")) {
						//inst.time--;
						//temp.A=temp.A+temp.vk;
						//temp.vj=0;
						//first=false;
						inst.time--;
						//CacheOutputPack out=c.getStoreLatency((int)temp.A, temp.vj+"");
						//inst.time=out.latency;
						inst.result = temp.vj;
						inst.execute = clock;
						nextWbList.add(inst);
						delList.add(i);
						//continue;
					}
				}
				else {
					inst.time--;
				} 
				/*if(inst.time==-1) {
					float result = register.read(inst.rs);
					nextWbList.add(inst);						
					
					inst.result = result;
					inst.execute = clock;
					//execList.remove(inst);
					delList.add(i);
				}*/
			} else if(opcode.equals("JMP")) {
					if(inst.time==1) {
						if(temp.qj.equals("")) {
							inst.time--;
							//continue;
						}
					} else {
						inst.time--;
					}
					if(inst.time==0) {
						pc=(int)temp.vj+(int)temp.vk+1+(int)temp.A;
						nextWbList.add(inst);
						//inst.result = result;
						inst.execute = clock;
						//execList.remove(inst);
						delList.add(i);
                                                if(before.equals("br"))
						    before="";
					}
				
			} else if(opcode.equals("RET")) {
				if(inst.time==1) {
					if(temp.qj.equals("")) {
						inst.time--;
						//continue;
					}
				} else {
					inst.time--;
				}
				if(inst.time==0) {
					pc=(int)temp.vj;
					nextWbList.add(inst);
					//inst.result = result;
					inst.execute = clock;
					//execList.remove(inst);
					delList.add(i);
					before="";
					
				}
			} else if(opcode.equals("JALR")) {
				if(inst.time==1) {
					if(temp.qj.equals("")) {
						inst.time--;
						pc=(int)temp.vj;
						//continue;
					}
				} else {
					inst.time--;
				}
				if(inst.time==0) {
					inst.result=temp.vk;
					nextWbList.add(inst);
					inst.execute=clock;
                                         if(before.equals("br"))
					before="";
					delList.add(i);
				}
			} else if(opcode.equals("BEQ")) {
				if(inst.time==ADDTIME) {
					if(temp.qj.equals("") && temp.qk.equals("")) {
						inst.time--;
						//continue;
					}
				} else {
					inst.time--;
				}
				if(inst.time==0) {
					if(temp.A<1e-5) {
						if(Math.abs(temp.vj-temp.vk)>1e-5) {
							inst.correctlyPredicted=false;
							nextWbList.add(inst);
							inst.execute=clock;
                                                         if(before.equals("br"))
							     before="";
							delList.add(i);
						} else {
							inst.correctlyPredicted=true;
							nextWbList.add(inst);
							inst.execute=clock;
                                                         if(before.equals("br"))
							before="";
							delList.add(i);
						}
					} else {
						if(Math.abs(temp.vj-temp.vk)>1e-5) {
							inst.correctlyPredicted=true;
							nextWbList.add(inst);
							inst.execute=clock;
                                                         if(before.equals("br"))
							before="";
							delList.add(i);
						} else {
							inst.correctlyPredicted=false;
							nextWbList.add(inst);
							inst.execute=clock;
                                                         if(before.equals("br"))
							before="";
							delList.add(i);
						}
					}
				}
			} 
			
		}
		for (int i = delList.size()-1; i >= 0; i--){
			execList.remove((int)delList.get(i));
		}
	}
	public void write() {
		ArrayList<Integer> delList = new ArrayList<Integer>();
		for (int j = 0; j < wbList.size(); j++){
			
			Instruction inst=wbList.get(j);
			String opcode=inst.opcode;
			if((opcode.equals("LW") || opcode.equals("ADD") || opcode.equals("JALR") || opcode.equals("ADDI")
					|| opcode.equals("MUL") || opcode.equals("DIV")) && !CDBAvailable) {
				continue;
			} else if((opcode.equals("LW") || opcode.equals("ADD") || opcode.equals("JALR") || opcode.equals("ADDI")
					|| opcode.equals("MUL") || opcode.equals("DIV"))) {
				CDBAvailable=false;
			}
			int rs=inst.station;
			//reservationStations
			int tempRob=reservationStations.entries[rs].dest;
			double result=inst.result;
			
			reservationStations.clearRSEntry(rs);
			rob.entries[tempRob].ready=true;
			rob.entries[tempRob].value=result;
			
			for(int i=0;i<reservationStations.entries.length;i++) {
				if(reservationStations.entries[i].qj.equals(""+tempRob)) {
					reservationStations.entries[i].vj=result;
					reservationStations.entries[i].qj="";
				}
				if(reservationStations.entries[i].qk.equals(""+tempRob)) {
					reservationStations.entries[i].vk=result;
					reservationStations.entries[i].qk="";
				}
			}
			inst.write=clock;
			delList.add(j);
			nextCommitList.add(inst);
		}
		CDBAvailable=true;
		for (int i = delList.size()-1; i >= 0; i--){
			wbList.remove((int)delList.get(i));
		}
		return;
		
	}
	public void commit() {
		ArrayList<Integer> delList = new ArrayList<Integer>();
		boolean found=false;
		for(int i=0;i<commitList.size();i++) {
			if(found) break;
			if(commitList.get(i).rob==rob.head) {
				found=true;
				if(rob.entries[rob.head].ready) {
					ROBEntry temp=rob.peekEntry();
					Instruction inst=temp.inst;
					String d=temp.Dest;
					boolean isthereDest=true;
					if(d.equals("") || inst.opcode.equals("SW")) {
						isthereDest=false;
					}
					if(inst.opcode.equals("BEQ")) {
						if(!inst.correctlyPredicted) {
							rob=new ROB(ROBEntries);
							//register.
							try {
								reservationStations=new RS(ADDRS, MULRS, LDRS, STRS, n);
							} catch (Exception e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							register.clearRegisters();
							execList.clear();
							commitList.clear();
							nextToExec=null;
							wbList.clear();
							nextCommitList.clear();
							nextWbList.clear();
							if(inst.branchtaken) {
								pc=inst.pc+1;
							} else {
								pc=inst.pc+1+(int)Double.parseDouble(inst.imm);
							}
						}
						else {
							rob.getEntry();
							commitList.remove(i);
						}
						inst.commit=clock;
						doneInstructions.add(new Done(inst.fetch,inst.issue,inst.execute,inst.write,
								inst.commit,inst.instString,inst.opcode));
						
						
					} else if(inst.opcode.equals("SW")) {
						if(inst.commit==0) {
							//System.out.println(temp.Dest);
							//System.out.println(temp.value);
							
							int t=Datac.getStoreLatency((int)Double.parseDouble(temp.Dest), ""+temp.value).latency;
							inst.commit=clock+t;
						}
						else if(inst.commit==clock) {
							rob.getEntry();
							doneInstructions.add(new Done(inst.fetch,inst.issue,inst.execute,inst.write,
									inst.commit,inst.instString,inst.opcode));
							commitList.remove(i);
						}	else return;
					} else if(isthereDest){
						register.write(temp.Dest, (float)temp.value);
						rob.getEntry();
						inst.commit=clock;
						doneInstructions.add(new Done(inst.fetch,inst.issue,inst.execute,inst.write,
								inst.commit,inst.instString,inst.opcode));
						commitList.remove(i);
						
					} else {
						rob.getEntry();
						inst.commit=clock;
						doneInstructions.add(new Done(inst.fetch,inst.issue,inst.execute,inst.write,
								inst.commit,inst.instString,inst.opcode));
						commitList.remove(i);
						
					}
					//temp.
					if(isthereDest)
					if(register.getStation(temp.Dest)==temp.number) {
						register.setStation(temp.Dest, -1);
						register.setIdle(temp.Dest);
					}
					
					
				}
			}
			
		}
		
		
		
	}
	public int schedule(int type){
		int i = 0;
		switch (type){
		//add
		case 1:
			for (i=0; i<n; i++){
				if ( !reservationStations.entries[i].busy && reservationStations.entries[i].type==3){
					return i;
				}
			}
			break;
		//mult	
		case 2:
			for (i=0; i<n; i++){
				if ( !reservationStations.entries[i].busy && reservationStations.entries[i].type==4){
					return i;
				}
			}
			break;
		//load	
		case 3:
			for (i=0; i<n; i++){
				if ( !reservationStations.entries[i].busy && reservationStations.entries[i].type==1){
					return i;
				}
			}
			break;
		//store	
		case 4:
			for (i=0; i<n; i++){
				if ( !reservationStations.entries[i].busy && reservationStations.entries[i].type==2){
					return i;
				}
			}
			break;
		}
		return -1;	
	
	}
	public static void main(String[] args) throws IOException {
		file="/C:/Users/Lydia/Documents/NetBeansProjects/MicroProjectFinal/Prog.in";
		ADDRS=1;
		MULRS=1;
		LDRS=1;
		ADDTIME=1;
		MULTIME=1;
		
		ROBEntries=6;
		n=ADDRS+MULRS+LDRS;
		basePC=0;
		InstructionBufferSize= 6;
		
	/*	noDataCaches=3;
		dmS=32;
		dmLatency=1;
		
		noInstCache=3;
		imS=32;
		imLatency=1;
		
		 Main.dc1S = 8;
         Main.dc1L = 1;
         Main.dc1M = 1;
         Main.dc1HWPolicy = 1;
         Main.dc1MWPolicy = 1;
         Main.dc1Latency = 1;

         //Data cache2 dimensions
         Main.dc2S = 8;
         Main.dc2L = 1;
         Main.dc2M = 1;
         Main.dc2HWPolicy = 1;
         Main.dc2MWPolicy = 1 ;
         Main.dc2Latency = 1;

         //Data cache3 dimensions
         Main.dc3S = 8;
         Main.dc3L = 1;
         Main.dc3M = 1;
         Main.dc3HWPolicy = 1;
         Main.dc3MWPolicy = 1 ;
         Main.dc3Latency = 1;
         
         
         Main.ic1S = 8;
         Main.ic1L = 1;
         Main.ic1M = 1;
         Main.ic1HWPolicy = 0;
         Main.ic1MWPolicy = 0;
         Main.ic1Latency = 1;

         //Instruction cache2 dimensions
         Main.ic2S = 8;
         Main.ic2L = 1;
         Main.ic2M = 1;
         Main.ic2HWPolicy = 0;
         Main.ic2MWPolicy = 0;
         Main.ic2Latency = 1;

         //Instruction cache3 dimensions
         Main.ic3S = 8;
         Main.ic3L = 1;
         Main.ic3M = 1;
         Main.ic3HWPolicy = 0;
         Main.ic3MWPolicy = 0;
         Main.ic3Latency = 1; */
		
		Main main=new Main();
		Datam.StoreWord(1, "1");
		Datam.StoreWord(2, "2");
		main.loadCode();
		main.runAll();
		for(int i=0;i<doneInstructions.size();i++) {
			Done done=doneInstructions.get(i);
			System.out.println(done.inst+":            "+done.fetch+"  "+done.issue+"  "+done.execute+"  "+done.write+"  "+done.commit);
		} 
		System.out.println("R1   "+register.read("R1"));
		System.out.println("R2   "+register.read("R2"));
		System.out.println("R5   "+register.read("R5"));
		System.out.println("R6   "+register.read("R6"));
		System.out.println("R7   "+register.read("R7"));
		System.out.println(Datac.getLoadLatency(2).word);
	}
	
}
